#pragma once
#include <fastdds/dds/domain/DomainParticipant.hpp>
#include <fastdds/dds/topic/Topic.hpp>
#include <fastdds/dds/publisher/DataWriter.hpp>
#include <fastdds/dds/publisher/Publisher.hpp>
#include <fastdds/dds/subscriber/Subscriber.hpp>
#include <fastdds/dds/subscriber/DataReader.hpp>
#include <fastdds/dds/subscriber/qos/DataReaderQos.hpp>
#include <unordered_map>
#include <fastdds/dds/publisher/DataWriterListener.hpp>
#include <fastdds/dds/subscriber/DataReaderListener.hpp>

namespace DDS_PARAMETER {
	struct DDS_Attribute;
	enum ReturnCode;
	enum Reliability;
	enum PublishMode;
	enum HistoryKind;
	enum Durability;
};
class DDS_Message {
	class SubscriberListener : public eprosima::fastdds::dds::DataReaderListener
	{
	public:
		SubscriberListener();
		~SubscriberListener() override;
		void on_data_available(
			eprosima::fastdds::dds::DataReader* reader) override;

		void on_subscription_matched(
			eprosima::fastdds::dds::DataReader* reader,
			const eprosima::fastdds::dds::SubscriptionMatchedStatus& info) override;
		void set_callback(uint32_t ref);
		bool is_matched();
		std::string get_last_msg();
		uint64_t get_timestamp();
	private:
		int matched_;
		//void (*func_callback)(unsigned int* index, unsigned long long *timestamp, char** msg);
		uint32_t call_back_ref_;
		std::string last_msg_;
		uint64_t timestamp_;
	};
	class PublisherListener : public eprosima::fastdds::dds::DataWriterListener
	{
	public:
		PublisherListener();
		~PublisherListener() override;

		void on_publication_matched(
			eprosima::fastdds::dds::DataWriter* writer,
			const eprosima::fastdds::dds::PublicationMatchedStatus& info) override;
		bool has_subscriber();
	private:
		int matched_;
		bool firstConnected_;
	};
	struct PubSub_Ptr {
		union
		{
			struct Sub {
				eprosima::fastdds::dds::Subscriber* subscriber_;
				eprosima::fastdds::dds::DataReader* reader_;
			}sub;
			struct Pub {
				eprosima::fastdds::dds::Publisher* publisher_;
				eprosima::fastdds::dds::DataWriter* writer_;
			}pub;
		}sub_pub;
		eprosima::fastdds::dds::Topic* topic_;
		std::shared_ptr<PublisherListener> wt_listener;
		std::shared_ptr<SubscriberListener> rd_listener;
		bool is_writer_;
		PubSub_Ptr() :topic_(nullptr), is_writer_(false) {
			sub_pub.pub.publisher_ = nullptr;
			sub_pub.pub.writer_ = nullptr;
			sub_pub.sub.reader_ = nullptr;
			sub_pub.sub.subscriber_ = nullptr;
		}
	};
public:
	DDS_Message();
	~DDS_Message();
	DDS_PARAMETER::ReturnCode init_by_xml_profile(const DDS_PARAMETER::DDS_Attribute& dds_attr, std::string xml_path, uint32_t domain_id, std::string participant_profile,
		std::string topic_profile, std::string rw_profile, std::string topic_name, bool is_writer);
	DDS_PARAMETER::ReturnCode init_all_by_default_qos(unsigned short trans_type, unsigned int domain_id, std::string participant_name, std::string topic_name);
	
	DDS_PARAMETER::ReturnCode init(const DDS_PARAMETER::DDS_Attribute& dds_attr, bool is_writer);
	DDS_PARAMETER::ReturnCode publish(std::string topic_name, unsigned int index, unsigned long long timestamp, std::string message);
	std::string get_msg(std::string topic_name);
	uint64_t get_timestamp(std::string topic_name);
	bool is_matched(std::string topic_name);
private:
	DDS_PARAMETER::ReturnCode create_reader_writer(const DDS_PARAMETER::DDS_Attribute& dds_attr, PubSub_Ptr& pub_sub_ptr, bool is_writer);
	DDS_PARAMETER::ReturnCode create_pub_sub(PubSub_Ptr& pub_sub_ptr, bool is_writer);

	eprosima::fastdds::dds::ReliabilityQosPolicyKind get_reliability(DDS_PARAMETER::Reliability reliability);
	eprosima::fastdds::dds::PublishModeQosPolicyKind get_pub_mode(DDS_PARAMETER::PublishMode mode);
	eprosima::fastdds::dds::DurabilityQosPolicyKind_t get_durability(DDS_PARAMETER::Durability durability);
	eprosima::fastdds::dds::HistoryQosPolicyKind get_history_kind(DDS_PARAMETER::HistoryKind);

	void free_pub_sub(PubSub_Ptr &);
private:
	eprosima::fastdds::dds::DomainParticipant* participant_;
	std::unordered_map<std::string, PubSub_Ptr> topic_pub_sub;
	eprosima::fastdds::dds::TypeSupport type_;
};